package ro.ubbcluj.cs.damate.webutils;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Map;

import static java.lang.String.*;
import static javax.faces.application.FacesMessage.*;
import static org.apache.commons.lang.StringUtils.*;

/**
 * JSFUtil are methods that go deep inside JSF/Spring to get stuff out of it.
 */
public class JSFUtil {
    private static Logger log = Logger.getLogger(JSFUtil.class);

    public static String returnResource(String resourceName, String resourceContent, String contentType) {
        try {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();

            response.setContentType(contentType);
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setHeader("Content-disposition", "attachment; filename=" + resourceName);

            response.getOutputStream().write(resourceContent.getBytes());

            facesContext.responseComplete();
        } catch (IOException e) {
            e.printStackTrace();
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "unable to write", "unable to write"));
        }

        return null;
    }

    public static String returnResource(String resourceName, InputStream resourceContent, String contentType) {
        ServletOutputStream outputStream = null;

        try {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();

            response.setContentType(contentType);
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setHeader("Content-disposition", "attachment; filename=" + resourceName);

            outputStream = response.getOutputStream();
            IOUtils.copy(resourceContent, outputStream);

        } catch (IOException e) {
            e.printStackTrace();
            facesContext().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "unable to write", "unable to write"));
        } finally {
            facesContext().responseComplete();
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(resourceContent);
        }

        return null;
    }

    public static HttpServletResponse httpResponse() {
        return (HttpServletResponse) facesContext().getExternalContext().getResponse();
    }

    public static HttpServletRequest httpRequest() {
        // either a JSF call, either a Spring servlet call.
        if (facesContext() != null) {
            return (HttpServletRequest) facesContext().getExternalContext().getRequest();
        }
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        
        if (requestAttributes == null) {
            return null;
        }
        
        return requestAttributes.getRequest();
    }

    public static FacesContext facesContext() {
        return FacesContext.getCurrentInstance();
    }

    public static ServletContext servletContext() {
        return httpSession().getServletContext();
    }

    public static HttpSession httpSession() {
        return httpRequest().getSession(true);
    }

    public static String getRequestParameter(String name) {
        HttpServletRequest request = httpRequest();

        if (request == null) {
            return null;
        }

        return request.getParameter(name);
    }

    public static Map<String, String[]> getRequestParameterMap() {
        HttpServletRequest request = httpRequest();

        if (request == null) {
            return null;
        }

        return request.getParameterMap();
    }

    public static void addGlobalMessage(FacesMessage.Severity severity, String message) {
        if (facesContext() != null) {
            facesContext().addMessage(null, new FacesMessage(severity, message, ""));
        } else {
            log.warn(format("Faces message with severity %s, %s", severity.toString(), message));
        }
    }
    
    public static void addGlobalMessage(FacesMessage.Severity severity, String message, String details) {
        if (facesContext() != null) {
            facesContext().addMessage(null, new FacesMessage(severity, message, details));
        } else {
            log.warn(format("Faces message with severity %s, message: %s, details: %s",
                    severity.toString(), message, details));
        }
    }

    public static void addMessage(String id, FacesMessage.Severity severity, String message, String details) {
        facesContext().addMessage(id, new FacesMessage(severity, message, details));
    }

    public static void addMessage(String id, FacesMessage.Severity severity, String message) {
        facesContext().addMessage(id, new FacesMessage(severity, message, ""));
    }

    public static void addGlobalMessageKey(FacesMessage.Severity severity, String key) {
        if (facesContext() != null) {
            facesContext().addMessage(null, new FacesMessage(severity, MessageProvider.getValue(key), ""));
        } else {
            log.warn(format("Faces message with severity %s, key: %s", severity.toString(), key));
        }
    }

    public static void addErrorMessageKey(String key) {
        if (facesContext() != null) {
            JSFUtil.addGlobalMessage(FacesMessage.SEVERITY_ERROR, MessageProvider.getValue(key));
        } else {
            log.warn(format("Faces message with key %s", key));
        }
    }

    public static void addErrorMessageKey(String key, String details) {
        if (facesContext() != null) {
            JSFUtil.addGlobalMessage(FacesMessage.SEVERITY_ERROR, MessageProvider.getValue(key), details);
        } else {
            log.warn(format("Faces message with key %s, details: %s", key, details));
        }
    }

    public static void displayMessage(String msg) {
        if (isNotEmpty(msg)) {
            String[] message = msg.split(":");
            if (message.length >= 2) {
                String[] params = null;
                if (message.length > 2) {
                    params = Arrays.copyOfRange(message, 2, message.length);
                }
                JSFUtil.addGlobalMessage(getSeverity(message[0]), MessageProvider.getValue(message[1], params));
            }
        }
    }

    public static FacesMessage.Severity getSeverity(String value) {
        if (value.equals(SEVERITY_INFO.toString())) {
            return SEVERITY_INFO;
        } else if (value.equals(SEVERITY_WARN.toString())) {
            return SEVERITY_WARN;
        } else if (value.equals(SEVERITY_ERROR.toString())) {
            return SEVERITY_ERROR;
        } else if (value.equals(SEVERITY_FATAL.toString())) {
            return SEVERITY_FATAL;
        }
        return SEVERITY_ERROR;
    }

    public static String getExceptionToString() {
        Throwable ex = (Throwable) httpRequest().getAttribute("javax.servlet.error.exception");

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ex.printStackTrace(new PrintStream(outputStream));

        return new String(outputStream.toByteArray());
    }

    public static String redirect(String targetURL) {
        try {
            if (facesContext() != null) {
                facesContext().getExternalContext().redirect(targetURL);
            } else {
                log.warn("Unable to redirect to " + targetURL + " since no faces context is present.");
            }
        } catch (IOException e) {
            throw new IllegalArgumentException("Unable to redirect to " + targetURL, e);
        }
        
        return null;
    }
}
